﻿using System;
using MathParser.DataModel;

namespace MathParser {
    public partial class Parser {
        public string JoinForIntegralDifferentialCalculation(bool fuehrendeMinusEinsFaktorAufloesen) {
            var ergebnisOld = this.Expression;
            var ergebnis = this.Expression;
            string term1;
            double z1 = 0;
            double z2 = 0;
            double e;
            switch (this.OpTyp) {
            case Operation.Potenz:
            case Operation.Plus:
            case Operation.Multi:
            case Operation.Minus:
            case Operation.Divi: {
                    term1 = this.ChildLeft.JoinForIntegralDifferentialCalculation(true);
                    string term2 = this.ChildRight.JoinForIntegralDifferentialCalculation(true);
                    this.ChildLeft = new Parser();
                    this.ChildLeft.Parse(term1);
                    this.ChildRight = new Parser();
                    this.ChildRight.Parse(term2);
                    // vorsichthalber mal ein Zwischenergebnis bilden
                    string term1Tmp = term1;
                    string term2Tmp = term2;
                    switch (this.OpTyp) {
                    case Operation.Plus:
                        AddBracketsForPlusAndMinusOperations(ref term1Tmp);
                        AddBracketsForPlusAndMinusOperations(ref term2Tmp);
                        ergebnis = term1Tmp + "+" + term2Tmp;
                        this.OpTyp = Operation.Plus;
                        break;
                    case Operation.Minus:
                        AddBracketsForPlusAndMinusOperations(ref term1Tmp);
                        AddBracketsForPlusAndMinusOperations(ref term2Tmp);
                        ergebnis = term1Tmp + "-" + term2Tmp;
                        this.OpTyp = Operation.Minus;
                        break;
                    case Operation.Multi:
                        AddBracketsForPlusAndMinusOperations(ref term1Tmp);
                        AddBracketsForPlusAndMinusOperations(ref term2Tmp);
                        ergebnis = term1Tmp + "*" + term2Tmp;
                        this.OpTyp = Operation.Multi;
                        break;
                    case Operation.Divi:
                        AddBracketsForBasicOperations(ref term1Tmp);
                        AddBracketsForBasicOperations(ref term2Tmp);
                        ergebnis = term1Tmp + "/" + term2Tmp;
                        this.OpTyp = Operation.Divi;
                        break;
                    case Operation.Potenz:
                        AddBracketsForBasicOperations(ref term1Tmp);
                        AddBracketsForBasicOperations(ref term2Tmp);
                        ergebnis = term1Tmp + "^" + term2Tmp;
                        this.OpTyp = Operation.Potenz;
                        break;
                    }
                    #region 2 Zahlen (a+b=c, z.B. 6+4=10)
                    // ***
                    if ((this.ChildLeft.OpTyp == Operation.Zahl) && (this.ChildRight.OpTyp == Operation.Zahl))// z.B. 6+4=10
                    {
                        {
                            z1 = double.Parse(term1);
                            z2 = double.Parse(term2);
                            switch (this.OpTyp) {
                            case Operation.Plus:
                                e = z1 + z2;
                                break;
                            case Operation.Minus:
                                e = z1 - z2;
                                break;
                            case Operation.Multi:
                                e = z1 * z2;
                                break;
                            case Operation.Divi:
                                e = z1 / z2; // hier muss später noch geprüft werden, ob die Division nicht zu einem Nährungswert führt
                                break;
                            default:
                                e = Math.Pow(z1, z2);
                                break;
                            }
                            this.OpTyp = Operation.Zahl;
                            ergebnis = e.ToString();
                        }
                    }
                    // ***
                    #endregion
                    else if ((this.OpTyp == Operation.Plus) && (term1 == term2)) // a+a = 2*a
                    {
                        this.OpTyp = Operation.Multi;
                        AddBracketsForPlusAndMinusOperations(ref term1);
                        ergebnis = "2*" + term1;
                    }
                    else if ((this.OpTyp == Operation.Minus) && (term1 == term2)) // a-a=0
                    {
                        this.OpTyp = Operation.Zahl;
                        ergebnis = "0";
                    }
                    else if ((this.OpTyp == Operation.Multi) && (term1 == term2)) // a*a = a^2
                    {
                        this.OpTyp = Operation.Potenz;
                        AddBracketsForPlusAndMinusOperations(ref term1);
                        if ((this.Expression.StartsWith("sin(")) ||
                            (this.Expression.StartsWith("cos(")) ||
                            (this.Expression.StartsWith("tan(")) ||
                            (this.Expression.StartsWith("sqrt(")) ||
                            (this.Expression.StartsWith("lg(")) ||
                            (this.Expression.StartsWith("ln(")) ||
                            (this.Expression.StartsWith("lb(")) ||
                            (this.Expression.StartsWith("("))
                            ) {
                            term1 = "(" + term1 + ")";
                        }
                        ergebnis = term1 + "^2";
                    }
                    else if ((this.OpTyp == Operation.Potenz) &&     // (a^n)^m =a^(n*m)
                             (this.ChildLeft.OpTyp == Operation.Potenz)
                             && (this.ChildLeft.ChildRight.OpTyp == Operation.Zahl) &&
                             (this.ChildRight.OpTyp == Operation.Zahl)) {
                        double expo1 = double.Parse(this.ChildLeft.ChildRight.Expression);
                        double expo2 = double.Parse(term2);
                        double expoNew = expo1 * expo2;
                        string expoNewString = expoNew.ToString();
                        AddBracketsForPlusAndMinusOperations(ref expoNewString);
                        string basis = this.ChildLeft.ChildLeft.Expression;
                        AddBracketsForPlusAndMinusOperations(ref basis);
                        ergebnis = basis + "^" + expoNewString;
                    }
                    else if ((this.ChildLeft.OpTyp == Operation.Multi) && // 3*a+6*a=9*a
                             (this.ChildRight.OpTyp == Operation.Multi) &&
                             ((this.OpTyp == Operation.Plus) || (this.OpTyp == Operation.Minus))) {
                        bool zusammenFassenMoeglich = true;
                        string termTmp1 = "";
                        string termTmp2 = "";
                        if (this.ChildLeft.ChildLeft.OpTyp == Operation.Zahl) {
                            z1 = double.Parse(this.ChildLeft.ChildLeft.Expression);
                            termTmp1 = this.ChildLeft.ChildRight.Expression;
                        }
                        else if (this.ChildLeft.ChildRight.OpTyp == Operation.Zahl) {
                            z1 = double.Parse(this.ChildLeft.ChildRight.Expression);
                            termTmp1 = this.ChildLeft.ChildLeft.Expression;
                        }
                        else
                            zusammenFassenMoeglich = false;

                        if (zusammenFassenMoeglich) {
                            if (this.ChildRight.ChildLeft.OpTyp == Operation.Zahl) {
                                z2 = double.Parse(this.ChildRight.ChildLeft.Expression);
                                termTmp2 = this.ChildRight.ChildRight.Expression;
                            }
                            else if (this.ChildRight.ChildRight.OpTyp == Operation.Zahl) {
                                z2 = double.Parse(this.ChildRight.ChildRight.Expression);
                                termTmp2 = this.ChildRight.ChildLeft.Expression;
                            }
                            else
                                zusammenFassenMoeglich = false;
                        }
                        if (zusammenFassenMoeglich && (termTmp1 == termTmp2)) {
                            if (this.OpTyp == Operation.Plus) e = z1 + z2;
                            else e = z1 - z2;
                            ergebnis = e + "*" + termTmp1;
                            this.OpTyp = Operation.Multi;
                        }
                    }
                    else if ((this.ChildLeft.OpTyp == Operation.Multi) &&
                             ((this.OpTyp == Operation.Plus) || (this.OpTyp == Operation.Minus)) &&
                             ((this.ChildRight.Expression == this.ChildLeft.ChildLeft.Expression) || (this.ChildRight.Expression == this.ChildLeft.ChildRight.Expression))) // 3*a+a=4*a
                    {
                        bool zusammenFassenMoeglich = true;
                        string termTmp1 = "";
                        if (this.ChildLeft.ChildLeft.OpTyp == Operation.Zahl) {
                            z1 = double.Parse(this.ChildLeft.ChildLeft.Expression);
                            termTmp1 = this.ChildLeft.ChildRight.Expression;
                        }
                        else if (this.ChildLeft.ChildRight.OpTyp == Operation.Zahl) {
                            z1 = double.Parse(this.ChildLeft.ChildRight.Expression);
                            termTmp1 = this.ChildLeft.ChildLeft.Expression;
                        }
                        else
                            zusammenFassenMoeglich = false;
                        if (zusammenFassenMoeglich && (termTmp1 == this.ChildRight.Expression)) {
                            if (this.OpTyp == Operation.Plus) e = z1 + 1;
                            else e = z1 - 1;
                            ergebnis = e + "*" + termTmp1;
                            this.OpTyp = Operation.Multi;
                        }
                    }
                    else if ((this.ChildRight.OpTyp == Operation.Multi) &&
                             ((this.OpTyp == Operation.Plus) || (this.OpTyp == Operation.Minus)) &&
                             ((this.ChildLeft.Expression == this.ChildRight.ChildLeft.Expression) || (this.ChildLeft.Expression == this.ChildRight.ChildRight.Expression))) // a+3*a=4*a
                    {
                        bool zusammenFassenMoeglich = true;
                        string termTmp1 = "";
                        if (this.ChildRight.ChildLeft.OpTyp == Operation.Zahl) {
                            z1 = double.Parse(this.ChildRight.ChildLeft.Expression);
                            termTmp1 = this.ChildRight.ChildRight.Expression;
                        }
                        else if (this.ChildRight.ChildRight.OpTyp == Operation.Zahl) {
                            z1 = double.Parse(this.ChildRight.ChildRight.Expression);
                            termTmp1 = this.ChildRight.ChildLeft.Expression;
                        }
                        else
                            zusammenFassenMoeglich = false;
                        if (zusammenFassenMoeglich && (termTmp1 == this.ChildLeft.Expression)) {
                            if (this.OpTyp == Operation.Plus) e = z1 + 1;
                            else e = 1 - z1;
                            ergebnis = e + "*" + termTmp1;
                            this.OpTyp = Operation.Multi;
                        }
                    }
                    else if ((this.OpTyp == Operation.Multi) && // 1*a = a
                             (this.ChildLeft.OpTyp == Operation.Zahl) &&
                             (double.Parse(term1) == 1)) {
                        ergebnis = term2;
                        this.OpTyp = this.ChildRight.OpTyp;
                    }
                    else if ((this.OpTyp == Operation.Multi) && // a*1 = a
                             (this.ChildRight.OpTyp == Operation.Zahl) &&
                             (double.Parse(term2) == 1)) {
                        ergebnis = term1;
                        this.OpTyp = this.ChildLeft.OpTyp;
                    }
                    else if ((this.OpTyp == Operation.Multi) && // -1*a=-a
                             (this.ChildLeft.Expression == "-1") && fuehrendeMinusEinsFaktorAufloesen) {
                        AddBracketsForPlusAndMinusOperations(ref term2);
                        ergebnis = "-" + term2;
                        this.OpTyp = Operation.Minus;
                    }
                    else if ((this.OpTyp == Operation.Multi) && // 0*a=0
                             ((this.ChildLeft.Expression == "0") || (this.ChildRight.Expression == "0"))) {
                        ergebnis = "0";
                        this.OpTyp = Operation.Zahl;
                    }
                    else if ((this.OpTyp == Operation.Divi) &&  // 0/a = 0
                             (this.ChildLeft.OpTyp == Operation.Zahl) &&
                             (double.Parse(term1) == 0)) {
                        ergebnis = "0";
                        this.OpTyp = Operation.Zahl;
                    }
                    else if ((this.OpTyp == Operation.Potenz) && (term2 == "1"))  // a^1=a
                    {
                        ergebnis = term1;
                        this.OpTyp = this.ChildLeft.OpTyp;
                    }
                    else if ((this.OpTyp == Operation.Potenz) && (term2 == "0"))  // a^0=1
                    {
                        ergebnis = "1";
                        this.OpTyp = Operation.Zahl;
                    }
                    else if ((this.OpTyp == Operation.Minus) && (term1 == "0"))  // 0-a=-a
                    {
                        ergebnis = "-" + term2;
                        this.OpTyp = Operation.Minus;
                    }
                    else if ((this.OpTyp == Operation.Plus) && (term2 == "0"))   // a+0=a
                    {
                        ergebnis = term1;
                        this.OpTyp = this.ChildLeft.OpTyp;
                    }
                    else if ((this.OpTyp == Operation.Plus) && (term1 == "0"))  // 0+a=a
                    {
                        ergebnis = term2;
                        this.OpTyp = this.ChildRight.OpTyp;
                    }
                    else if ((this.OpTyp == Operation.Divi) && (term1 == term2)) // a/a=1
                    {
                        ergebnis = "1";
                        this.OpTyp = Operation.Zahl;
                    }
                    else if ((this.OpTyp == Operation.Divi) && // sin(x)/cos(x)=tan(x)
                             (term1 == "sin(x)") &&
                             (term2 == "cos(x)")) {
                        ergebnis = "tan(x)";
                        this.OpTyp = Operation.Tangens;
                    }
                }
                break;
            case Operation.Sinus:
                term1 = this.ChildLeft.JoinForIntegralDifferentialCalculation(true);
                this.ChildLeft = new Parser();
                this.ChildLeft.Parse(term1);
                ergebnis = "sin(" + term1 + ")";
                break;
            case Operation.Cosinus:
                term1 = this.ChildLeft.JoinForIntegralDifferentialCalculation(true);
                this.ChildLeft = new Parser();
                this.ChildLeft.Parse(term1);
                ergebnis = "cos(" + term1 + ")";
                break;
            case Operation.Tangens:
                term1 = this.ChildLeft.JoinForIntegralDifferentialCalculation(true);
                this.ChildLeft = new Parser();
                this.ChildLeft.Parse(term1);
                ergebnis = "tan(" + term1 + ")";
                break;
            case Operation.Sqrt:
                term1 = this.ChildLeft.JoinForIntegralDifferentialCalculation(true);
                this.ChildLeft = new Parser();
                this.ChildLeft.Parse(term1);
                ergebnis = "sqrt(" + term1 + ")";
                break;
            case Operation.Fakultät:
                term1 = this.ChildLeft.JoinForIntegralDifferentialCalculation(true);
                this.ChildLeft = new Parser();
                this.ChildLeft.Parse(term1);
                if (term1.Length > 1)
                    term1 = "(" + term1 + ")";
                ergebnis = term1 + "!";
                break;
            case Operation.Lb:
                term1 = this.ChildLeft.JoinForIntegralDifferentialCalculation(true);
                this.ChildLeft = new Parser();
                this.ChildLeft.Parse(term1);
                ergebnis = "lb(" + term1 + ")";
                break;
            case Operation.Ln:
                term1 = this.ChildLeft.JoinForIntegralDifferentialCalculation(true);
                this.ChildLeft = new Parser();
                this.ChildLeft.Parse(term1);
                ergebnis = "ln(" + term1 + ")";
                break;
            case Operation.Lg:
                term1 = this.ChildLeft.JoinForIntegralDifferentialCalculation(true);
                this.ChildLeft = new Parser();
                this.ChildLeft.Parse(term1);
                ergebnis = "lg(" + term1 + ")";
                break;
            }
            if (ergebnis.Length > (ergebnisOld.Length + 3)) // nicht "verschlimmbessern..."
                ergebnis = ergebnisOld;
            return ergebnis;
        }
        private static void AddBracketsForBasicOperations(ref string term) {
            var parTerm = new Parser();
            parTerm.Parse(term);
            if ((term.Length > 1) &&
                (((parTerm.OpTyp == Operation.Plus) || (parTerm.OpTyp == Operation.Minus)) ||
                 ((parTerm.OpTyp == Operation.Multi) || (parTerm.OpTyp == Operation.Divi)) ||
                 (term.StartsWith("-"))
                )) {
                term = "(" + term + ")";
            }

        }
        private static void AddBracketsForPlusAndMinusOperations(ref string term) {
            var parTerm = new Parser();
            parTerm.Parse(term);
            if ((term.Length > 1) &&
                (((parTerm.OpTyp == Operation.Plus) || (parTerm.OpTyp == Operation.Minus)) ||
                (term.StartsWith("-")))
                ) {
                term = "(" + term + ")";
            }

        }
    }
}